package com.webkit.web;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lamfire.logger.Logger;
import com.lamfire.utils.ObjectFactory;
import com.webkit.web.action.Action;

public class Dispatcher
{
  private static final Logger LOGGER = Logger.getLogger(Dispatcher.class);

  private static ThreadLocal<Dispatcher> dispacherInstance = new ThreadLocal<Dispatcher>();
  private static ThreadLocal<ActionContext> actionContextInstance = new ThreadLocal<ActionContext>();
  private static InvocationVisitor visitor = new InvocationVisitor();
  private String defaultEncoding;
  private String multipartSaveDir;
  private long multipartLimitSize = 10000000L;
  private String actionRoot;

  public static Dispatcher getInstance()
  {
    return (Dispatcher)dispacherInstance.get();
  }

  static void setInstance(Dispatcher instance) {
    dispacherInstance.set(instance);
  }

  static void setActionContext(ActionContext context) {
    actionContextInstance.set(context);
  }

  public static ActionContext getActionContext() {
    return (ActionContext)actionContextInstance.get();
  }

  static ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response, ServletContext context) {
    ActionContext ac = ActionContext.createActionContext(context, request, response);
    setActionContext(ac);
    return ac;
  }

  public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws ServletException, IOException, ActionException, ClassNotFoundException
  {
    String servletPath = request.getServletPath();
    String actionCls = getActionClassName(servletPath);

    ActionContext ac = createActionContext(request, response, context);
    Action action = null;

    ObjectFactory factory = new ObjectFactory(actionCls);
    try
    {
      action = (Action)factory.newInstance();
      ObjectFactory.bindData(action, ac.getParameters());
      action.init();
    } catch (Exception e) {
      LOGGER.warn("init " + actionCls + " instance exception:" + e.getMessage());
    }

    try
    {
      action.accept(visitor);
    } catch (Exception e) {
      LOGGER.error("execute action exception." + e);
      ac.sendRedirect("/505.jsp");
    } finally {
      if (action != null)
        action.destroy();
    }
  }

  protected String getActionClassName(String servletPath)
  {
    int index = servletPath.lastIndexOf("/");
    int dotIndex = servletPath.lastIndexOf(".");
    String actionName = servletPath.substring(index + 1);
    if (dotIndex > 0) {
      actionName = actionName.substring(0, dotIndex - 1);
    }

    String nameSpace = null;
    if (servletPath.indexOf("/") != index) {
      nameSpace = servletPath.substring(1, index);
      nameSpace = nameSpace.replace('/', '.');
    }

    String simpleClassName = actionName.substring(0, 1).toUpperCase() + actionName.substring(1) + "Action";
    StringBuilder builder = new StringBuilder();

    if ((this.actionRoot != null) && (!"".equals(this.actionRoot.trim()))) {
      builder.append(this.actionRoot.trim());
      builder.append('.');
    }
    if ((nameSpace != null) && (!"".equals(nameSpace.trim()))) {
      builder.append(nameSpace.trim());
      builder.append('.');
    }
    builder.append(simpleClassName);

    return builder.toString();
  }

  private String getMultipartSaveDir(ServletContext servletContext) {
    String saveDir = this.multipartSaveDir.trim();

    if ((saveDir == null) && ("".equals(saveDir))) {
      File tempdir = (File)servletContext.getAttribute("javax.servlet.context.tempdir");
      LOGGER.info("Unable to find 'request.multipart.tempdir' property setting. Defaulting to javax.servlet.context.tempdir");

      if (tempdir != null) {
        saveDir = tempdir.toString();
        this.multipartSaveDir = saveDir;
      }

      return this.multipartSaveDir;
    }

    File multipartSaveDir = new File(saveDir);
    if ((!multipartSaveDir.exists()) && (!multipartSaveDir.mkdirs())) {
      String logMessage = "Could not find create multipart save directory '" + saveDir + "'.";
      LOGGER.warn(logMessage);
    }

    LOGGER.debug("saveDir=" + saveDir);

    return saveDir;
  }

  public void prepare(HttpServletRequest request, HttpServletResponse response) {
    String encoding = "utf-8";
    if (this.defaultEncoding != null)
      encoding = this.defaultEncoding;
    try
    {
      request.setCharacterEncoding(encoding);
      response.setCharacterEncoding(encoding);
      response.setHeader("Pragma", "No-cache");
      response.setHeader("Cache-Control", "no-cache");
      response.setDateHeader("Expires", 0L);
    } catch (UnsupportedEncodingException e) {
      LOGGER.warn(e.getMessage());
    }
  }

  public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
    if ((request instanceof WebKitRequestWrapper)) {
      return request;
    }

    String content_type = request.getContentType();
    if ((content_type != null) && (content_type.indexOf("multipart/form-data") != -1)) {
      MultiPartRequestImpl multi = new MultiPartRequestImpl();
      multi.setMaxSize(this.multipartLimitSize);
      request = new MultiPartRequestWrapper(multi, request, getMultipartSaveDir(servletContext));
    }

    request = new WebKitRequestWrapper(request);

    return request;
  }

  public void setMultipartLimitSize(long limit) {
    this.multipartLimitSize = limit;
  }

  public void setMultipartSaveDir(String dir) {
    this.multipartSaveDir = dir;
  }

  public void setDefaultEncoding(String defaultEncoding) {
    this.defaultEncoding = defaultEncoding;
  }

  public void setActionRoot(String actionRoot) {
    this.actionRoot = actionRoot;
  }
}